/*
 * Copyright (c) 2020, yifengling0. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. No personal names or organizations' names associated with the
 *    Atomthreads project may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


#include "iosdrv.h"
#include "sio.h"
#include "kassert.h"

static int sio_read(FD fd, char* buffer, size_t maxbytes)
{
	CRITICAL_STORE;
	SioBase* sio = (SioBase*)fd;

	CRITICAL_START();
	int count = ringGet(&sio->Protected.ops->rxRing, buffer, maxbytes);
	CRITICAL_END();
	
	return count;
}

static int sio_write(FD fd, const char* buffer, size_t nbytes)
{
	struct SioBase* sio = (SioBase*)fd;
	int count = nbytes;
	CRITICAL_STORE;

	if( sio->Protected.param->mode == enSioBufferedSend){
		CRITICAL_START();
		count = ringPut(&sio->Protected.ops->txRing, buffer, nbytes);
		CRITICAL_END();

		if (count > 0) {
			sio->Protected.ops->BufferedSend(sio);
		}
	}else{
        atomMutexGet(&sio->Protected.ops->lock, 0);
		sio->Protected.ops->BlockingSend(sio, buffer, nbytes);
        atomMutexPut(&sio->Protected.ops->lock);
	}

	return count;
}

static int sio_ioctl(FD fd, int function, void* arg)
{
	struct SioBase* sio = (SioBase*)fd;

	int ret = sio->Protected.ops->Ioctl(sio, function, arg);
	uint8_t *isEmpty = (uint8_t*)arg;

	if( ret == -1 ){
		switch(function){
			case SIO_RX_ISEMPTY:
			*isEmpty = ringIsEmpty(&sio->Protected.ops->rxRing) ?  1 : 0;
			ret = OK;
			break;
			default:
			ret = NG;
		}
	}

	return (ret);
}


void SioCreate(SioBase* sio, const char *name, SIO_PARAM *param, SioOps *ops, char *rxBuffer, uint32_t rxSize, char *txBuffer, uint32_t txSize)
{
	sio->super.popen = NULL;
	sio->super.pioctl = sio_ioctl;
	sio->super.pread = sio_read;
	sio->super.pwrite = sio_write;
	sio->super.pclose = NULL;

	ringCreate(&ops->rxRing, rxBuffer, rxSize);
    atomMutexCreate(&ops->lock);
	
	if( param->mode == enSioBufferedSend){
		ringCreate(&ops->txRing, txBuffer, txSize);
	}

	sio->Protected.ops = ops;
	sio->Protected.param = param;

	IosDrvInstall(&sio->super, name);
}